﻿using System;
using System.Collections.Generic;
using System.Threading;
using Chambills.Present.Core;
using NUnit.Framework;
using Present.Commands.Async.Actions;
using Present.Commands.Async.Actions.Decorators;
using Rhino.Mocks;

namespace Present.Commands.Tests
{
    [TestFixture]
    public class CompositeAsyncActionTestCase : TestCase
    {


        [Test]
        public void PrepareWorkIsRunForAllTargets()
        {
            var innerTargets = new[]
                                   {
                                       StubObservableAction(),
                                       StubObservableAction()
                                   };
            
            var target = new CompositeAsyncAction(Console.WriteLine, innerTargets);
            target.PrepareWork(null);
            innerTargets.ForEach(t => t.AssertWasCalled(x => x.PrepareWork(null)));
        }

     

        [Test]
        public void CanRunMultipleTargetsConcurrently()
        {
            EnableAsyncCalls();
            var results = new List<object>();

            var target = new CompositeAsyncAction(Console.WriteLine, new IObservableCommandAction[] 
                { 
                                CreateIntTarget(100, results.Add), 
                                CreateStringTarget(300, results.Add), 
                                CreateIntTarget(400, results.Add), 
                                CreateStringTarget(10, results.Add), 
                                CreateIntTarget(50, results.Add) 
                });

            target.DoWork(target.PrepareWork(null));
            Assert.AreEqual(5, results.Count);
        }




        [Test]
        public void CanExecuteDependsOnAllTargets()
        {
            var innerTarget1 = MockRepository.GenerateStub<IObservableCommandAction>();
            var innerTarget2 = MockRepository.GenerateStub<IObservableCommandAction>();
            innerTarget1.Stub(x => x.CanExecute(0)).Return(true);
            innerTarget2.Stub(x => x.CanExecute(0)).Return(false);
            var target = new CompositeAsyncAction(Console.WriteLine, new[]
                                                                                {
                                                                                    innerTarget1,
                                                                                    innerTarget2
                                                                                });
            Assert.IsFalse(target.CanExecute(0));
        }

        [Test]
        public void CanExecuteIfAllTargetsCanExecute()
        {
            var innerTarget = MockRepository.GenerateStub<IObservableCommandAction>();
            innerTarget.Stub(x => x.CanExecute(0)).Return(true);
            var target = new CompositeAsyncAction(Console.WriteLine, new[]
                                                                                {
                                                                                    innerTarget
                                                                                });
           Assert.IsTrue(target.CanExecute(0));
        }

        [Test]
        public void CanExecuteChangedLinkedToInnerActions()
        {
            var innerTarget1 = MockRepository.GenerateStub<IObservableCommandAction>();
            var innerTarget2 = MockRepository.GenerateStub<IObservableCommandAction>();
            var target = new CompositeAsyncAction(Console.WriteLine, new[]
                                                                                {
                                                                                    innerTarget1, innerTarget2
                                                                                });

            bool fired = false;
            target.CanExecuteChanged += (sender, e) => fired = true;
            innerTarget2.Raise(x => x.CanExecuteChanged += null, innerTarget2, EventArgs.Empty);
            Assert.IsTrue(fired);

        }

        private ObservableAsyncActionDecorator<object, int> CreateIntTarget(int sleep, Action<object> handleSuccess)
        {
            return new DelegateAsyncAction<object, int>(x => DoWork(sleep), e => { }, n => handleSuccess(n)).ToObservable();
        }


        private ObservableAsyncActionDecorator<object, string> CreateStringTarget(int sleep, Action<object> handleSuccess)
        {
            return new DelegateAsyncAction<object, string>(x => DoWork(sleep).ToString(), e => { }, handleSuccess).ToObservable();
        }

        private IObservableCommandAction StubObservableAction()
        {
            var o = MockRepository.GenerateStub<IObservableCommandAction>();
            o.Stub(x => x.Subscribe(null)).IgnoreArguments().Return(new DisposableAction(() => { }));
            return o;
        }

        private int DoWork(int sleep)
        {
            Thread.Sleep(sleep);
            return sleep;
        }
    }
}
